package com.tjbaobao.mp4lib.mp4info;


import com.tjbaobao.mp4lib.info.CustomData;
import com.tjbaobao.mp4lib.info.Mp4InfoConstant;
import com.tjbaobao.mp4lib.tools.ByteTools;
import com.tjbaobao.mp4lib.tools.FileTools;
import com.tjbaobao.mp4lib.tools.HexConvertTools;

import java.io.File;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;

/**
 * MP4合并器
 * @author TJbaobao
 *
 */
public class Mp4Merger {
	private static final String DEF_CHARSET_NAME= "iso8859-1";
	private String pathTjbb = "",outPath = "";
	
	private CustomData customData = null;

	private long indexMDat ;
	private long sizeMDat;
	private long sizeFileMP4,sizeHead,sizeFoot,indexHead,indexFoot;
	private long positionAddMdat = 0;
	private long readedSize = 0;

	private OnProgressListener onProgressListener ;
	
	public Mp4Merger(String pathTjbb,String outPath,OnProgressListener onProgressListener) {
		this.pathTjbb = pathTjbb;
		this.outPath = outPath;
		this.onProgressListener = onProgressListener;
		getMp4Info(pathTjbb,outPath);
	}
	
	public static Mp4Merger build(String pathTjbb,String outPath,OnProgressListener onProgressListener)
	{
		return new Mp4Merger(pathTjbb,outPath,onProgressListener);
	}
	
	
	private void getMp4Info(String pathTjbb,String outPath)
	{
		File file = new File(pathTjbb);
		if(file.exists())
		{
			byte[] bytes = FileTools.Reader.readFileToBytes(0, (int)file.length(), pathTjbb);
			String nameTj =  HexConvertTools.bytesToStr(ByteTools.readBytes(bytes, 0, 4));
			int sizeTj = HexConvertTools.bytesToInt(ByteTools.readBytes(bytes, 4, 4));
			
			if(nameTj.equals(Mp4InfoConstant.HEAD_NAME)&&sizeTj>0)
			{
				byte[] bytesHead = null;
				byte[] bytesFoot = null;
				byte[] bytesMdatSize = null;
				int position = 8;
				while(position<file.length())
				{
					int size = HexConvertTools.bytesToInt(ByteTools.readBytes(bytes, position+4, 4));
					byte[] bytesBox = ByteTools.readBytes(bytes, position, size);
					String name = HexConvertTools.bytesToStr(ByteTools.readBytes(bytesBox, 0, 4));
					if(name.equals(Mp4InfoConstant.HEAD_CUSTOM_NAME))
					{
						customData = readCustomData(bytesBox);
					}
					else if(name.equals(Mp4InfoConstant.HEAD_HEAD_NAME))
					{
						bytesHead = readBoxValue(bytesBox);
					}
					else if(name.equals(Mp4InfoConstant.HEAD_FOOT_NAME))
					{
						bytesFoot = readBoxValue(bytesBox);
					}
					else if(name.equals(Mp4InfoConstant.HEAD_MDAT_SIZE_NAME))
					{
						bytesMdatSize = readBoxValue(bytesBox);
					}
					position+=size;
				}
				sizeHead = getBytesLength(bytesHead);
				sizeFoot = getBytesLength(bytesFoot);
				sizeMDat = HexConvertTools.bytesToInt(bytesMdatSize);
				indexHead = 0;
				indexFoot = sizeHead+sizeMDat;
				indexMDat = sizeHead ;
				sizeFileMP4 = sizeHead+sizeFoot+sizeMDat;
				FileTools.createNullFile(outPath,sizeFileMP4);//创建和原MP4等大的空文件
				//开始填充头尾文件
				FileTools.Writer.writeFile(bytesHead,0,outPath);
				if(sizeFoot!=0)
				{
					FileTools.Writer.writeFile(bytesFoot,indexFoot,outPath);
				}
				positionAddMdat  = indexMDat;
				readedSize = sizeFoot+sizeHead;
				onProgress(readedSize,sizeFileMP4);
			}
		}
		else
		{
			System.out.println("文件不存在");
		}
	}

	public Mp4Merger addMDat(String pathMdatPart)
	{
		if(pathMdatPart==null)
		{
			return null;
		}
		File file = new File(pathMdatPart);
		if(file.exists())
		{
			FileTools.coverFile(positionAddMdat,pathMdatPart,outPath);
			positionAddMdat +=file.length();
			readedSize+=file.length();
			onProgress(readedSize,sizeFileMP4);
			return this;
		}
		return null;
	}
	
	private CustomData readCustomData(byte[] bytes)
	{
		CustomData customData = new CustomData();
		int sizeName = HexConvertTools.bytesToInt(ByteTools.readBytes(bytes, 8+4, 4));
		int sizeData = HexConvertTools.bytesToInt(ByteTools.readBytes(bytes, 8+sizeName+4, 4));
		byte[] byteName = ByteTools.readBytes(bytes, 8, sizeName);
		ArrayList<String> nameList = readNameList(byteName);
		byte[] byteData = ByteTools.readBytes(bytes, 8+sizeName, sizeData);
		return readDataList(byteData,nameList,customData);
	}
	
	private ArrayList<String> readNameList(byte[] bytes)
	{
		String name = HexConvertTools.bytesToStr(ByteTools.readBytes(bytes, 0, 4));
		int size = HexConvertTools.bytesToInt(ByteTools.readBytes(bytes, 4, 4));
		int position = 8;
		ArrayList<String> strList = new ArrayList<>();
		while(position<bytes.length)
		{
			int sizeSub = HexConvertTools.bytesToInt(ByteTools.readBytes(bytes, position+4, 4));
			if(size>=8)
			{
				byte[] bytesValue = ByteTools.readBytes(bytes, position+8, sizeSub-8);
				strList.add(HexConvertTools.bytesToStr(bytesValue));
			}
			
			position+=sizeSub;
		}
		return strList ;
	}
	
	private CustomData readDataList(byte[] bytes,ArrayList<String> nameList,CustomData customData)
	{
		int size = HexConvertTools.bytesToInt(ByteTools.readBytes(bytes, 4, 4));
		int position = 8;
		int i = 0;
		while(position<bytes.length)
		{
			String name = nameList.get(i);
			try {
				int sizeName = name.getBytes(DEF_CHARSET_NAME).length;
				String valueName = HexConvertTools.bytesToStr(ByteTools.readBytes(bytes, position, sizeName));
				int sizeSub = HexConvertTools.bytesToInt(ByteTools.readBytes(bytes, position+sizeName, 4));
				if(size>=sizeName+4)
				{
					byte[] bytesValue = ByteTools.readBytes(bytes, position+sizeName+4, sizeSub-4-sizeName);
					customData.addData(name, bytesValue);
				}
				position+=sizeSub;
				i++;
			} catch (UnsupportedEncodingException e) {
				e.printStackTrace();
			}
		}
		return customData ;
	}
	
	private byte[] readBoxValue(byte[] bytes)
	{
		int size = HexConvertTools.bytesToInt(ByteTools.readBytes(bytes, 4, 4));
		if(size>=8)
		{
			return ByteTools.readBytes(bytes, 8, size-8);
		}
		return null;
	}
	
	private int getBytesLength(byte[] bytes)
	{
		return bytes==null?0:bytes.length;
	}

	public OnProgressListener getOnProgressListener() {
		return onProgressListener;
	}

	public void setOnProgressListener(OnProgressListener onProgressListener) {
		this.onProgressListener = onProgressListener;
	}
	
	private void onProgress(long readed,long size)
	{
		if(onProgressListener!=null)
		{
			onProgressListener.onProgress(readed, size);
		}
	}

	public String getPathTjbb() {
		return pathTjbb;
	}

	public void setPathTjbb(String pathTjbb) {
		this.pathTjbb = pathTjbb;
	}

	public String getOutPath() {
		return outPath;
	}

	public void setOutPath(String outPath) {
		this.outPath = outPath;
	}

	public static String getDefCharsetName() {
		return DEF_CHARSET_NAME;
	}

	public CustomData getCustomData() {
		return customData;
	}

	public long getIndexMDat() {
		return indexMDat;
	}

	public long getSizeMDat() {
		return sizeMDat;
	}

	public long getSizeFileMP4() {
		return sizeFileMP4;
	}

	public long getSizeHead() {
		return sizeHead;
	}

	public long getSizeFoot() {
		return sizeFoot;
	}

	public long getIndexHead() {
		return indexHead;
	}

	public long getIndexFoot() {
		return indexFoot;
	}

	public long getPositionAddMdat() {
		return positionAddMdat;
	}

	public long getReadedSize() {
		return readedSize;
	}
	
}
